//
// Created by wlk12 on 2023/3/17.
//

#include <iostream>
#include <functional>

auto funStaticVariable()
{
    static int n = 9;
    int m = 9;
    auto lf = [=](){

        std::cout << "n=" << n  << " m=" << m << std::endl;
    };
    ++m;
    ++n;
    return lf;
}


class A
{
public:

    [[nodiscard]] auto fun() const
    {

        return [a=a](){
            std::cout << "A, a=" << a << std::endl;
            return a;
        };
    }

    int a = 10;
};

template<typename T, typename=std::function<void()>>
void call1(T&& fun)
{
    std::cout << "call1 template T&& fun size: " << sizeof(fun) << std::endl;
    fun();
}

void call2(const std::function<void()>& fun)
{
    std::cout << "call2 std::function<void()>& fun size: " << sizeof(fun) << std::endl;
    fun();
}

class MoveParam
{
public:
    int32_t data{1};

    MoveParam() = default;
    ~MoveParam()
    {
        std::cout << "MoveParam destruct: " << data << std::endl;
        this->data = 0;
    }
    MoveParam(const MoveParam& ) = delete;
    MoveParam& operator=(const MoveParam&) = delete;

    MoveParam(MoveParam&& copy) noexcept
        :data(copy.data)
    {
        copy.data = 0;
    }

    MoveParam& operator=(MoveParam&& right) noexcept
    {
        this->data = right.data;

        right.data = 0;
        return *this;
    }
};

int main()
{
    auto lf = funStaticVariable();
    lf();

    A a;
    auto af = a.fun();
    af();


    call1([](){
        int n = 0;
        ++n;
    });
    call2([](){
        int n = 0;
        ++n;
    });

    {
        MoveParam param;
        param.data = 10;
        auto fun = [p = std::move(param)]() mutable {
            std::cout << "Call Lambda MoveParam: " << p.data << std::endl;
            p.data = 100;
        };
        fun();
        auto p = std::move(fun);
//        std::function<void()> fun2 = std::move(fun); // 移动语义的lambda无法放入function对象
    }
    {
        std::shared_ptr<MoveParam> spParam = std::make_shared<MoveParam>();
        spParam->data = 20;
        auto fun = [p = std::move(spParam)]{
            std::cout << "Call Lambda shared_ptr MoveParam: " << p->data << std::endl;
            p->data = 200;
        };
        std::function<void()> fun2 = std::move(fun);
        fun2();
        fun2 = nullptr;
        int n = 0;
        ++n;
    }

    {
        std::function<void()> fun = [](){ std::cout << "fun" << std::endl; };
        auto fun2 = fun;
//        std::cout << "fun == fun2 ? : " << (fun == fun2) << std::endl; // compile error

        auto fun3 = [](){ std::cout << "fun" << std::endl; };
        auto fun4 = fun3;
        auto fun5 = [](){ std::cout << "fun" << std::endl; };

        // std::cout << "fun3 == fun4 ? : " << (fun4 == fun3) << std::endl;
        // std::cout << "fun3 == fun5 ? : " << (fun3 == fun5) << std::endl;  // compile error

    }

    {
        auto lambda = [](){ std::cout << "fun" << std::endl; };
        std::function<void()> fun = lambda;
        std::cout << "lambda typeid: " << typeid(lambda).name()  << std::endl
                  << "std::function typeid: " << typeid(fun).name() << std::endl;
    }

    {
        std::unique_ptr<int> num = std::make_unique<int>(5);
        auto lambda = [num = std::move(num)]() {
            std::cout << "Move catch lambda: " << *num << std::endl;
        };
        // std::function<void()> fun = std::move(lambda); // compile error,移动语义的捕获无法赋值给 function 对象
        lambda();
    }

    return  0;
}
